﻿using Acme.BookStore.Com;
using AutoMapper.Internal.Mappers;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
using Volo.Abp.Users;
using Volo.Abp;
using Acme.BookStore.DoMainService;

namespace Acme.BookStore
{
    /// <summary>
    /// 字典类父类
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TEntityDto"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TGetListInput"></typeparam>
    /// <typeparam name="TCreateInput"></typeparam>
    /// <typeparam name="TUpdateInput"></typeparam>
    public class AcmeComBaseAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> : AcmeAppService//, IGLisComBaseAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
         where TEntity : class, IEntity<TKey>
         where TEntityDto : IEntityDto<TKey>
    {
        /// <summary>
        /// 查询缓存
        /// </summary>
        //public IDistributedCache<string> Cache { get; set; }
        // public IDataCacheDomainService CacheData { get; set; }
        public IDataFilter _dataFilter { get; set; }
        private readonly string CacheKey;
        /// <summary>
        /// 仓储
        /// </summary>
        protected IRepository<TEntity, TKey> Repository;
        public AcmeComBaseAppService(IRepository<TEntity, TKey> repository) : base()
        {
            Repository = repository;
            Type t = typeof(TEntity);
            //CacheKey = t.Name + "Dto";
        }

        #region 缓存应用
        /// <summary>
        /// 清空缓存
        /// </summary>
        //public void ClearCache()
        //{
        //    try
        //    {

        //        Type t = typeof(TEntity);
        //        IDataCacheDomainService CacheData = LazyServiceProvider.LazyGetService<IDataCacheDomainService>();
        //        string table = t.Name;
        //        CacheData.ClearCache(table + "Dto");
        //        CacheData.ClearCache(table + "Core");
        //        CacheData.ClearCache(table);

        //    }
        //    catch (Exception ex)
        //    {
        //        Logger.LogError("ClearCache,清空缓存异常" + ex.Message + "," + ex.StackTrace);
        //    }
        //}


        /// <summary>
        /// 从缓存中获取所有列表
        /// </summary>
        /// <returns></returns>
        //public virtual async Task<OutputBaseDto<List<TEntityDto>>> GetCacheAllList()
        //{
        //    IDataCacheDomainService CacheData = LazyServiceProvider.LazyGetService<IDataCacheDomainService>();
        //    Type t = typeof(TEntity);
        //    CacheData.AddKeysAsync(t.Name, CacheKey);
        //    OutputBaseDto<List<TEntityDto>> output = new OutputBaseDto<List<TEntityDto>>()
        //    {
        //        Data = System.Text.Json.JsonSerializer.Deserialize<List<TEntityDto>>(
        //          await Cache.GetOrAddAsync(CacheKey, async () =>
        //          {
        //              return await GetCacheStrAsync();
        //          }, () =>
        //          {
        //              return new Microsoft.Extensions.Caching.Distributed.DistributedCacheEntryOptions()
        //              {
        //                  SlidingExpiration = TimeSpan.FromHours(GLisConsts.CacheHour)
        //              };
        //          }))
        //    };
        //    output.Msg = "";
        //    return output;
        //}
        /// <summary>
        /// 对象序列化成字符串 缓存到redis
        /// </summary>
        /// <returns></returns>
        private async Task<string> GetCacheStrAsync()
        {
            var AllList = await GetDataBaseAsync();


            string strAll = System.Text.Json.JsonSerializer.Serialize(AllList);
            return strAll;
        }
        /// <summary>
        /// 刷新缓存
        /// </summary>
        public virtual void RefreshManyAsync()
        {
            //var cache = LazyServiceProvider.LazyGetRequiredService<IDistributedCache<List<ComHospital>>>();
            //cache.Refresh();
        }
        #endregion

        #region 列表查询
        /// <summary>
        /// 查询所有数据列表 
        /// </summary>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<List<TEntityDto>>> GetAllListAsync()
        {
            //  string key = Repository.GetType().Name;

            //   ComBaseDto baseDto = _cache.GetOrAdd("ComTestMethod", () => GetDataBase());
            OutputBaseDto<List<TEntityDto>> output = new OutputBaseDto<List<TEntityDto>>()
            {
                Msg = "",
                Data = await GetDataBaseAsync()
                //Cache.GetOrAddAsync(key, () => await GetDataBaseAsync()) 
            };
            return output;
        }
        /// <summary>
        /// 过滤掉停用和未审核的
        /// </summary>
        /// <returns></returns>
        private async Task<List<TEntityDto>> GetDataBaseAsync()
        {
            List<TEntity> entities = new List<TEntity>();
            var AllList = await Repository.GetListAsync();

            foreach (var item in AllList)
            {
                entities.Add(item);
            }

            return ObjectMapper.Map<List<TEntity>, List<TEntityDto>>(entities);

        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<PagedResultDto<TEntityDto>>> GetListAsync(TGetListInput input)
        {
            //var query = await CreateFilteredQueryAsync(input);

            //var totalCount = await AsyncExecuter.CountAsync(query);

            //query = ApplySorting(query, input);
            //query = ApplyPaging(query, input);

            //var entities = await AsyncExecuter.ToListAsync(query);
            //var entityDtos = await MapToGetListOutputDtosAsync(entities);

            //var res = new PagedResultDto<TEntityDto>(
            //    totalCount,
            //    entityDtos
            //);

            OutputBaseDto<PagedResultDto<TEntityDto>> output = new OutputBaseDto<PagedResultDto<TEntityDto>>();
            ////output.Data = await _crudApp.GetListAsync(input); ;
            //output.Data = res; output.Msg = "";
            return output;
        }


        /// <summary>
        /// 列表查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<List<TEntityDto>>> GetListNoPageAsync(TGetListInput input)
        {
            //var query = await CreateFilteredQueryAsync(input);

            //query = ApplySorting(query, input);

            //var entities = await AsyncExecuter.ToListAsync(query);
            //var entityDtos = await MapToGetListOutputDtosAsync(entities);

            OutputBaseDto<List<TEntityDto>> output = new OutputBaseDto<List<TEntityDto>>();
            //output.Data = await _crudApp.GetListAsync(input); ;
            //output.Data = entityDtos; output.Msg = "";
            return output;
        }
        #endregion

        #region 增删改查
        /// <summary>
        /// 根据id 删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<OutputBase> DeleteAsync(TKey id)
        {
            //ClearCache();
            OutputBase output = new OutputBase();
            await Repository.DeleteAsync(id);
            output.Msg = "删除操作成功!";
            return output;
        }

        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual async Task<OutputBase> Deletes(List<TKey> input)
        {
            //ClearCache();
            OutputBase output = new OutputBase();
            try
            {
                // List<int> ids = input.Select(p => p.Id).ToList();
                await Repository.DeleteManyAsync(input, true);
                output.Code = 0;
                output.Msg = "删除操作成功";
            }
            catch (Exception ex)
            {
                //  Logger.LogError(ex.Message);
                output.Code = 1;
                output.Msg = "删除操作失败:" + ex.Message;
            }
            return output;
        }
        /// <summary>
        /// 根据ID 返回实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<TEntity>> GetAsync(TKey id)
        {
            OutputBaseDto<TEntity> output = new OutputBaseDto<TEntity>();
            output.Data = await Repository.GetAsync(id);
            return output;
        }
        /// <summary>
        /// 根据ID 返回实体dto
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<TEntityDto>> GetDtoAsync(TKey id)
        {
            OutputBaseDto<TEntityDto> output = new OutputBaseDto<TEntityDto>();
            var entity = await Repository.GetAsync(id);
            output.Data = ObjectMapper.Map<TEntity, TEntityDto>(entity);
            return output;
        }

        /// <summary>
        /// 插入返回
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<TEntityDto>> CreateAsync(TCreateInput input)
        {
            OutputBaseDto<TEntityDto> output = new OutputBaseDto<TEntityDto>();

            //检查Code(编码)是否已被占用
            //bool isExist = await IsExistCode(input, 0);
            //if (isExist)
            //{
            //    output.Data = default(TEntityDto);
            //    output.Msg = "编码已使用，请修改后再提交";
            //    output.Code = (int)GLisAppServiceConsts.ErrorCode;
            //    return output;
            //}

            var entity = ObjectMapper.Map<TCreateInput, TEntity>(input);
            //ClearCache();
            //  Repository.Any(p=>p.WhereIf);
            await Repository.InsertAsync(entity, autoSave: true);

            output.Data = ObjectMapper.Map<TEntity, TEntityDto>(entity);
            output.Msg = "新增操作成功!";
            return output;
        }
        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="id"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<TEntityDto>> UpdateAsync(TKey id, TUpdateInput input)
        {
            OutputBaseDto<TEntityDto> output = new OutputBaseDto<TEntityDto>();

            //检查Code(编码)是否已被占用
            //bool isExist = await IsExistCode(input, 1);
            //if (isExist)
            //{
            //    output.Data = default(TEntityDto);
            //    output.Msg = "编码已使用，请修改后再提交";
            //    output.Code = (int)GLisAppServiceConsts.ErrorCode;
            //    return output;
            //}

            //ClearCache();
            var entity = await Repository.GetAsync(id);

            entity = ObjectMapper.Map<TUpdateInput, TEntity>(input, entity);
            await Repository.UpdateAsync(entity, autoSave: true);

            output.Data = ObjectMapper.Map<TEntity, TEntityDto>(entity);
            return output;
        }
        #endregion

        #region 字典审批
        /// <summary>
        /// 审核和拒绝审核方法
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        //public virtual async Task<OutputBase> AuditOrRefuseAsync(List<TEntityDto> input)
        //{
        //    ClearCache();
        //    OutputBase output = new OutputBase();
        //    try
        //    {

        //        var ids = input.Select(p => p.Id);
        //        List<TEntity> list = Repository.Where(p => ids.Contains(p.Id)).ToList();

        //        foreach (TEntity item in list)
        //        {
        //            if (!(item.GetType().GetInterfaces().Contains(typeof(IAuditComBase)))) continue;//如果不是审核方法
        //                                                                                            //  var Audit=  item as IAuditComBase;
        //                                                                                            // if (item.GetType().GetProperty("UnAuditReason") != null && input[0].GetType().GetProperty("UnAuditReason")  != null)
        //            string UnAuditReason = input[0].GetType().GetProperty("UnAuditReason").GetValue(input[0]) + "";
        //            item.GetType().GetProperty("UnAuditReason").SetValue(item, UnAuditReason); //item.GetType().GetProperty("UnAuditReason").SetValue(item, Audit.UnAuditReason, null);

        //            int AuditState = int.Parse(input[0].GetType().GetProperty("AuditState").GetValue(input[0]) + "");
        //            item.GetType().GetProperty("AuditState").SetValue(item, AuditState, null);


        //            if (item.GetType().GetProperty("AuditUserId") != null)
        //                item.GetType().GetProperty("AuditUserId").SetValue(item, CurrentUser.Id, null);

        //            if (item.GetType().GetProperty("AuditUserName") != null)
        //                item.GetType().GetProperty("AuditUserName").SetValue(item, CurrentUser.Name, null);

        //            if (item.GetType().GetProperty("AuditTime") != null)
        //                item.GetType().GetProperty("AuditTime").SetValue(item, DateTime.Now, null);

        //        }
        //        await Repository.UpdateManyAsync(list, true);
        //        output.Msg = "审核操作成功!";
        //        output.Code = 0;
        //    }
        //    catch (Exception ex)
        //    {
        //        Logger.LogError(ex.Message);
        //        output.Code = 1;
        //        output.Msg = "审核失败:" + ex.Message;
        //    }
        //    return output;
        //    //  return new OutputBase();
        //}
        #endregion

        #region 通用方法

        /// <summary>
        /// 过滤查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        //protected virtual async Task<IQueryable<TEntity>> CreateFilteredQueryAsync(TGetListInput input)
        //{
        //    var query = CreateFilteredQuery(input);
        //    if (!ReferenceEquals(query, Repository))
        //    {
        //        return query;
        //    }

        //    return await Repository.GetQueryableAsync();
        //}

        //protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetListInput input)
        //{
        //    //Try to sort query if available
        //    if (input is ISortedResultRequest sortInput)
        //    {
        //        if (!sortInput.Sorting.IsNullOrWhiteSpace())
        //        {
        //            return query.OrderBy(sortInput.Sorting);
        //        }
        //    }

        //    //IQueryable.Task requires sorting, so we should sort if Take will be used.
        //    if (input is ILimitedResultRequest)
        //    {
        //        return ApplyDefaultSorting(query);
        //    }

        //    //No sorting
        //    return query;
        //}

        /// <summary>
        /// Applies sorting if no sorting specified but a limited result requested.
        /// </summary>
        /// <param name="query">The query.</param>
        protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
        {
            if (typeof(TEntity).IsAssignableTo<IComSort>())
            {
                query = query.OrderBy(e => ((IComSort)e).Sort);
            }
            if (typeof(TEntity).IsAssignableTo<IHasCreationTime>())
            {
                return query.OrderBy(e => ((IHasCreationTime)e).CreationTime);
            }
            return query.OrderBy(e => e.Id);
            throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyReadOnlyAppService!");
        }

        /// <summary>
        /// Should apply paging if needed.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="input">The input.</param>
        protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetListInput input)
        {
            //Try to use paging if available
            if (input is IPagedResultRequest pagedInput)
            {
                return query.PageBy(pagedInput);
            }

            //Try to limit query result if available
            if (input is ILimitedResultRequest limitedInput)
            {
                return query.Take(limitedInput.MaxResultCount);
            }

            //No paging
            return query;
        }

        /// <summary>
        /// Maps a list of <typeparamref name="TEntity"/> to <typeparamref name="TGetListOutputDto"/> objects.
        /// It uses <see cref="MapToGetListOutputDtoAsync"/> method for each item in the list.
        /// </summary>
        protected virtual async Task<List<TEntityDto>> MapToGetListOutputDtosAsync(List<TEntity> entities)
        {
            var dtos = new List<TEntityDto>();

            foreach (var entity in entities)
            {
                dtos.Add(await MapToGetListOutputDtoAsync(entity));
            }

            return dtos;
        }

        /// <summary>
        /// Maps <typeparamref name="TEntity"/> to <typeparamref name="TGetListOutputDto"/>.
        /// It internally calls the <see cref="MapToGetListOutputDto"/> by default.
        /// It can be overriden for custom mapping.
        /// Overriding this has higher priority than overriding the <see cref="MapToGetListOutputDto"/>
        /// </summary>
        protected virtual Task<TEntityDto> MapToGetListOutputDtoAsync(TEntity entity)
        {
            return Task.FromResult(MapToGetListOutputDto(entity));
        }

        /// <summary>
        /// Maps <typeparamref name="TEntity"/> to <typeparamref name="TGetListOutputDto"/>.
        /// It uses <see cref="IObjectMapper"/> by default.
        /// It can be overriden for custom mapping.
        /// </summary>
        protected virtual TEntityDto MapToGetListOutputDto(TEntity entity)
        {
            return ObjectMapper.Map<TEntity, TEntityDto>(entity);
        }
        #endregion


        #region 导入导出

        /// <summary>
        /// 校验数据导入情况
        /// </summary>
        /// <param name="entityImportDtos">导入数据集合对象</param>
        /// <param name="verifyFunc">校验数据方法</param>
        /// <returns></returns>
        protected virtual async Task<OutputBase> VerifyImportData<TEntityImportDto>(object entityImportDtos, Action<TEntityImportDto, StringBuilder> verifyFunc)
            where TEntityImportDto : class
        {
            var importDtos = entityImportDtos as List<TEntityImportDto>;
            var output = new OutputBase();
            StringBuilder sb = new StringBuilder();
            foreach (var item in importDtos)
            {
                verifyFunc.Invoke(item, sb);
            }
            if (sb.Length > 0)
            {
                output.Code = (int)GLisAppServiceConsts.ErrorCode;
                output.Msg = sb.ToString();
            }
            return await Task.FromResult(output);
        }
        /// <summary>
        /// 保存数据导入列表
        /// </summary>
        /// <typeparam name="TEntityImportDto"></typeparam>
        /// <param name="entityImportDtos"></param>
        /// <param name="convertFunc"></param>
        /// <returns></returns>
        protected virtual async Task<OutputBaseDto<List<TEntityDto>>> SaveImportData<TEntityImportDto>(object entityImportDtos, Action<TEntityImportDto, List<TEntity>> convertFunc)
           where TEntityImportDto : class
        {
            var importDtos = entityImportDtos as List<TEntityImportDto>;
            var output = new OutputBaseDto<List<TEntityDto>>();
            var entitys = new List<TEntity>();

            foreach (var item in importDtos)
            {
                convertFunc.Invoke(item, entitys);
            }
            await Repository.InsertManyAsync(entitys, true);
            var entitydtos = ObjectMapper.Map<List<TEntity>, List<TEntityDto>>(entitys);
            output.Data = entitydtos;
            output.Msg = "数据保存成功！";
            return await Task.FromResult(output);
        }



        /// <summary>
        /// 转换实体为匿名对象
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        protected virtual dynamic ConvertToDynamic(object obj)
        {
            IDictionary<string, object> result = new ExpandoObject();

            foreach (PropertyDescriptor pro in TypeDescriptor.GetProperties(obj.GetType()))
            {
                result.Add(pro.Name, pro.GetValue(obj));
            }

            return result as ExpandoObject;
        }
        #endregion

        #region 树节点管理
        /// <summary>
        /// 父子节点返回
        /// </summary>
        /// <returns></returns>
        public virtual async Task<OutputBaseDto<ListResultDto<TEntityDto>>> GetRootListAsync()
        {
            return await Task.FromResult(new OutputBaseDto<ListResultDto<TEntityDto>>());
        }
        /// <summary>
        /// 获取当前选择的Id所有其他可以选择的父节点
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        protected virtual async Task<OutputBaseDto<List<TEntityDto>>> GetAllowParentNodes(TKey id)
        {
            //查询所有的数据
            var output = await GetAllListAsync();

            var queryList = output.Data;
            //根据当前Id查询Id下面所有的节点记录
            List<TEntityDto> childlst = new List<TEntityDto>();
            GetChildNodes(id, ref childlst, queryList);

            //删除子集
            childlst.ForEach(item => queryList.Remove(item));

            output.Data = queryList;
            return await Task.FromResult(output);
        }
        /// <summary>
        /// 获取子节点记录
        /// </summary>
        /// <param name="id"></param>
        /// <param name="childlst"></param>
        /// <param name="queryList"></param>
        private void GetChildNodes(TKey id, ref List<TEntityDto> childlst, List<TEntityDto> queryList)
        {
            foreach (var item in queryList)
            {

                if (item.GetType().GetProperty("ParentId").GetValue(item).Equals(id))
                {
                    childlst.Add(item);
                    GetChildNodes(item.Id, ref childlst, queryList);
                }
                if (item.Id.Equals(id))
                {
                    childlst.Add(item);
                }
            }
        }
        #endregion
    }
}